package plan

import (
	"fmt"
	"mini-sql/model"
	"mini-sql/storage"
)

type InsertPlanNode struct {
	statement  *model.InsertStatement
	storePool  *storage.StorePool
	tableStore storage.StoreInterface
}

func NewInsertPlanNode(insertStatement *model.InsertStatement, storePool *storage.StorePool) PlanNode {
	return &InsertPlanNode{
		statement: insertStatement,
		storePool: storePool,
	}
}

func (node *InsertPlanNode) Prepare() error {
	tableName := node.statement.TableRef.StrValue
	tableStore, err := node.storePool.GetTableStore(tableName)
	if err != nil {
		return err
	}

	node.tableStore = tableStore
	rows, err := node.buildRows()
	if err != nil {
		return err
	}

	for _, row := range rows {
		err = tableStore.InsertRow(row)
		if err != nil {
			return err
		}
	}
	return nil
}

func (node *InsertPlanNode) buildRows() ([]model.TableTuple, error) {
	fields := make([]string, 0)
	if len(node.statement.Fields) == 0 {
		return nil, fmt.Errorf("insert statement must set column names")
	}
	for _, item := range node.statement.Fields {
		if item.StrValue == "" {
			return nil, fmt.Errorf("field.StrValue is empty")
		}
		fields = append(fields, item.StrValue)
	}

	result := make([]model.TableTuple, 0)

	for _, row := range node.statement.ValueRowList {
		tuples := make([]model.TableField, 0)
		rowID := int64(0)

		for index, field := range row.Tuple {
			fieldName := fields[index]
			value, err := node.getExprValue(field)
			if err != nil {
				return nil, err
			}
			tuple := model.TableField{
				Name:  fieldName,
				Value: value,
			}
			tuples = append(tuples, tuple)
			if fieldName == model.TableRowIdName {
				int64Value, ok := value.(int64)
				if !ok {
					return nil, fmt.Errorf("expect int64 as id, got %v", value)
				}
				rowID = int64Value
				err := node.updateRowID(rowID)
				if err != nil {
					return nil, err
				}
			}
		}

		if rowID == 0 {
			var err error
			rowID, err = node.createRowID()
			if err != nil {
				return nil, err
			}
		}
		result = append(result, model.TableTuple{
			RowID:  rowID,
			Fields: tuples,
		})
	}

	return result, nil
}

func (node *InsertPlanNode) updateRowID(rowID int64) error {
	return node.tableStore.UpdateRowID(rowID)
}

func (node *InsertPlanNode) createRowID() (int64, error) {
	return node.tableStore.CreateRowID()
}

func (node *InsertPlanNode) getExprValue(field *model.Expr) (interface{}, error) {
	switch field.ExprType {
	case model.ExprTypeInt64:
		return field.IntValue, nil
	case model.ExprTypeStr:
		return field.StrValue, nil
	case model.ExprTypeFloat:
		return field.FloatValue, nil
	case model.ExprTypeBool:
		return bool(field.IntValue == 0), nil
	}
	return nil, fmt.Errorf("unsupported type %v", field.ExprType.String())
}

func (node *InsertPlanNode) GetNextTuple() (*model.TableTuple, error) {
	return nil, nil
}
